/**
 * @p.md
 * 
 * PostgreSQL 持久化示例
 * 
 * 注意：运行此示例需要安装 pg 依赖:
 * ```
 * npm install pg
 * ```
 * 
 * 并且需要启动 PostgreSQL 服务器，默认连接字符串为:
 * postgresql://postgres:postgres@localhost:5432/protoactor
 */

import {
  ActorSystem,
  Props,
  PID
} from '../src/actor';

import {
  PersistentActor,
  RecoverContext,
  CommandContext,
  EventType,
  PersistenceFactory,
  RecoveryStrategy,
  PostgreSQLProvider
} from '../src/persistence';

// 计数器命令类型
enum CounterCommandType {
  Increment = 'increment',
  Decrement = 'decrement',
  GetCount = 'get-count',
  Reset = 'reset',
  CreateSnapshot = 'create-snapshot'
}

// 计数器事件类型
enum CounterEventType {
  Incremented = 'incremented',
  Decremented = 'decremented',
  Reset = 'reset'
}

// 命令接口
interface CounterCommand {
  type: CounterCommandType;
  amount?: number;
}

// 事件接口
interface CounterEvent {
  type: CounterEventType;
  amount?: number;
}

// 计数器状态
interface CounterState {
  count: number;
}

/**
 * 持久化计数器 Actor
 */
class PersistentCounterActor extends PersistentActor {
  // 持久化 ID
  private _persistenceId: string;
  
  // 当前计数
  private count: number = 0;
  
  // 事件索引
  private eventIndex: number = 0;
  
  // 最后快照索引
  private lastSnapshotIndex: number = 0;
  
  // 快照间隔（事件数量）
  private readonly snapshotInterval: number = 10;
  
  /**
   * 构造函数
   * @param id 计数器 ID
   */
  constructor(id: string = 'postgresql') {
    super();
    this._persistenceId = `counter-${id}`;
  }
  
  /**
   * 获取持久化 ID
   */
  get persistenceId(): string {
    return this._persistenceId;
  }
  
  /**
   * 恢复事件处理
   * @param context 恢复上下文
   */
  receiveRecover(context: RecoverContext): void {
    const eventType = context.eventType;
    const event = context.event;
    
    if (eventType === EventType.Snapshot) {
      // 从快照恢复状态
      const state = event as CounterState;
      this.count = state.count;
      console.log(`[PostgreSQL] 从快照恢复状态: count = ${this.count}`);
    } else if (eventType === EventType.Event) {
      // 从事件恢复状态
      this.applyEvent(event as CounterEvent);
      this.eventIndex = context.eventIndex;
      console.log(`[PostgreSQL] 从事件恢复状态: index = ${this.eventIndex}, count = ${this.count}`);
    }
  }
  
  /**
   * 命令处理
   * @param context 命令上下文
   */
  receiveCommand(context: CommandContext): void {
    const command = context.message() as CounterCommand;
    
    switch (command.type) {
      case CounterCommandType.Increment:
        const incrementAmount = command.amount || 1;
        const incrementEvent: CounterEvent = {
          type: CounterEventType.Incremented,
          amount: incrementAmount
        };
        
        // 持久化增量事件
        context.persist(incrementEvent).then(() => {
          this.eventIndex++;
          this.applyEvent(incrementEvent);
          context.respond({ count: this.count });
          
          // 检查是否需要创建快照
          this.checkSnapshot(context);
        });
        break;
        
      case CounterCommandType.Decrement:
        const decrementAmount = command.amount || 1;
        const decrementEvent: CounterEvent = {
          type: CounterEventType.Decremented,
          amount: decrementAmount
        };
        
        // 持久化减量事件
        context.persist(decrementEvent).then(() => {
          this.eventIndex++;
          this.applyEvent(decrementEvent);
          context.respond({ count: this.count });
          
          // 检查是否需要创建快照
          this.checkSnapshot(context);
        });
        break;
        
      case CounterCommandType.Reset:
        const resetEvent: CounterEvent = {
          type: CounterEventType.Reset
        };
        
        // 持久化重置事件
        context.persist(resetEvent).then(() => {
          this.eventIndex++;
          this.applyEvent(resetEvent);
          context.respond({ count: this.count });
          
          // 检查是否需要创建快照
          this.checkSnapshot(context);
        });
        break;
        
      case CounterCommandType.GetCount:
        // 只读操作，不需要持久化
        context.respond({ count: this.count });
        break;
        
      case CounterCommandType.CreateSnapshot:
        // 手动创建快照
        this.takeSnapshot(context);
        break;
        
      default:
        console.log(`[PostgreSQL] 未知命令: ${JSON.stringify(command)}`);
        break;
    }
  }
  
  /**
   * 应用事件到状态
   * @param event 事件
   */
  private applyEvent(event: CounterEvent): void {
    switch (event.type) {
      case CounterEventType.Incremented:
        this.count += event.amount || 1;
        console.log(`[PostgreSQL] 增加计数: ${event.amount || 1}, 当前计数: ${this.count}`);
        break;
        
      case CounterEventType.Decremented:
        this.count -= event.amount || 1;
        console.log(`[PostgreSQL] 减少计数: ${event.amount || 1}, 当前计数: ${this.count}`);
        break;
        
      case CounterEventType.Reset:
        this.count = 0;
        console.log(`[PostgreSQL] 重置计数`);
        break;
    }
  }
  
  /**
   * 检查是否需要创建快照
   * @param context 命令上下文
   */
  private checkSnapshot(context: CommandContext): void {
    if (this.eventIndex - this.lastSnapshotIndex >= this.snapshotInterval) {
      this.takeSnapshot(context);
    }
  }
  
  /**
   * 创建快照
   * @param context 命令上下文
   */
  private takeSnapshot(context: CommandContext): void {
    const state: CounterState = {
      count: this.count
    };
    
    context.snapshot(state).then(() => {
      this.lastSnapshotIndex = this.eventIndex;
      console.log(`[PostgreSQL] 创建快照: count = ${this.count}, index = ${this.eventIndex}`);
    });
  }
  
  /**
   * 恢复完成回调
   */
  protected onRecoveryCompleted(context: any): void {
    console.log(`[PostgreSQL] 恢复完成: count = ${this.count}, index = ${this.eventIndex}`);
  }
}

/**
 * 发送命令并打印响应
 * @param system Actor 系统
 * @param pid Actor PID
 * @param command 命令
 */
async function sendCommand(system: ActorSystem, pid: PID, command: CounterCommand): Promise<void> {
  console.log(`[PostgreSQL] 发送命令: ${JSON.stringify(command)}`);
  const response = await system.root.requestFuture(pid, command, 1000);
  console.log(`[PostgreSQL] 响应: ${JSON.stringify(response)}`);
}

/**
 * 运行 PostgreSQL 持久化示例
 */
export async function runPostgreSQLExample(): Promise<void> {
  console.log('启动 PostgreSQL 持久化示例...');
  
  // 创建 Actor 系统
  const system = new ActorSystem();
  
  try {
    // 创建 PostgreSQL 持久化提供者
    const provider = PersistenceFactory.getInstance().createPostgreSQLProvider(
      'postgresql://postgres:postgres@localhost:5432/protoactor',
      'postgresql-example'
    );
    
    // 创建持久化 Actor
    const props = Props.fromProducer(() => new PersistentCounterActor());
    const pid = system.root.spawn(props);
    
    console.log('等待 Actor 恢复状态...');
    await new Promise(resolve => setTimeout(resolve, 1000));
    
    // 发送命令
    await sendCommand(system, pid, { type: CounterCommandType.GetCount });
    await sendCommand(system, pid, { type: CounterCommandType.Increment, amount: 5 });
    await sendCommand(system, pid, { type: CounterCommandType.Increment, amount: 3 });
    await sendCommand(system, pid, { type: CounterCommandType.Decrement, amount: 2 });
    await sendCommand(system, pid, { type: CounterCommandType.GetCount });
    
    // 创建快照
    await sendCommand(system, pid, { type: CounterCommandType.CreateSnapshot });
    
    // 停止 Actor
    await system.root.stop(pid);
    console.log('Actor 已停止');
    
    // 重新创建 Actor 以验证状态恢复
    console.log('重新创建 Actor 以验证状态恢复...');
    const newPid = system.root.spawn(props);
    
    // 等待恢复完成
    await new Promise(resolve => setTimeout(resolve, 1000));
    
    // 验证状态已恢复
    await sendCommand(system, newPid, { type: CounterCommandType.GetCount });
    
    // 继续操作
    await sendCommand(system, newPid, { type: CounterCommandType.Increment, amount: 10 });
    await sendCommand(system, newPid, { type: CounterCommandType.GetCount });
    
    // 重置计数器
    await sendCommand(system, newPid, { type: CounterCommandType.Reset });
    await sendCommand(system, newPid, { type: CounterCommandType.GetCount });
    
    // 停止 Actor
    await system.root.stop(newPid);
    
  } catch (error) {
    console.error('PostgreSQL 示例错误:', error);
  } finally {
    // 关闭 PostgreSQL 连接
    const provider = PersistenceFactory.getInstance().getProvider('postgresql-example') as PostgreSQLProvider;
    await provider.close();
    
    // 关闭 Actor 系统
    await system.terminate();
    console.log('PostgreSQL 持久化示例完成');
  }
}

/**
 * 运行示例
 */
export async function runExample(): Promise<void> {
  await runPostgreSQLExample();
}

// 如果直接运行此文件，则执行示例
if (require.main === module) {
  runExample().catch(console.error);
} 